package externalflame.systemdesign.newsfeed.service.impl;

import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import externalflame.systemdesign.newsfeed.dao.FriendDao;
import externalflame.systemdesign.newsfeed.dao.TweetDao;
import externalflame.systemdesign.newsfeed.dto.Page;
import externalflame.systemdesign.newsfeed.entity.Tweet;
import externalflame.systemdesign.newsfeed.service.TweetService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.util.*;


@ConditionalOnProperty(name = "app.tweetService", havingValue = "Pull")
@Service
public class PullTweetService implements TweetService {

    @Autowired
    TweetDao tweetDao;

    @Autowired
    FriendDao friendDao;

    IdentifierGenerator identifierGenerator = new DefaultIdentifierGenerator();


    private List<Tweet> kMergeSort(HashMap<Long, List<Tweet>> tweetMap, Long pageSize) {
        PriorityQueue<Tweet> Q = new PriorityQueue<>();
        HashMap<Long, Integer> indexArr = new HashMap<>();

        for (Map.Entry<Long, List<Tweet>> entry: tweetMap.entrySet()
             ) {
            if (!entry.getValue().isEmpty()) {
                Q.add(entry.getValue().get(0));
                indexArr.put(entry.getKey(), 1);
            } else {
                indexArr.put(entry.getKey(), 0);
            }
        }

        List<Tweet> ans = new ArrayList<>();
        while (!Q.isEmpty() && ans.size() < pageSize) {
            Tweet t = Q.poll();
            ans.add(t);
            int curr = indexArr.get(t.getUserId());
            if (curr < tweetMap.get(t.getUserId()).size()) {
                Q.add(tweetMap.get(t.getUserId()).get(curr++));
                indexArr.put(t.getUserId(), curr);
            }
        }
        return ans;
    }

    @Override
    public Page<Tweet> getMyTweets(Long userId, Long pageSize, Long maxId) {
        // 找到当前用户关注的所有人。
        List<Long> friends = friendDao.getFollowings(userId);
        friends.add(userId);
        // 对于所有用户，查询用户的小于等于 maxId 的前 pageSize 条推特。
        HashMap<Long, List<Tweet>> tweetMap = new HashMap<>();
        for(Long someUserId: friends) {
            List<Tweet> tweets = tweetDao.findTweets(someUserId, maxId, pageSize);
            if (tweets != null && !tweets.isEmpty()) {
                tweetMap.put(someUserId, tweets);
            }
        }
        // 使用 k 路归并，返回小于等于 maxId 的前 pageSize 条推特。
        List<Tweet> sortedTweets = kMergeSort(tweetMap, pageSize);

        Page<Tweet> ans = new Page<>();
        ans.setData(sortedTweets);
        ans.setPageSize(pageSize);
        if (!sortedTweets.isEmpty()) {
            maxId = sortedTweets.get(sortedTweets.size()-1).getId() + 1;
        } else {
            maxId = null;
        }
        if (maxId != null) {
            ans.setMaxId(maxId.toString());
        }
        return ans;
    }


    @Override
    public Tweet postTweet(Long userId, String content) {
        Long id = identifierGenerator.nextId(Tweet.class).longValue();
        // 数据库做读写分离，就需要指定 getTweetById 必须在 Master 节点查询。因为 Slave 节点读取可能滞后。
        tweetDao.add(id, userId, content, new Date());
        return tweetDao.getTweetById(id);
    }

    @Override
    public List<Tweet> getTimeline(Long userId, Long maxId, Long pageSize) {
        return tweetDao.findTweets(userId, maxId, pageSize);
    }
}
